package frsf.cidisi.exercise.arqueologo.search;

import Clases.Aldea;
import Clases.Cofre;
import Clases.Mundo;
import Interfaz.Index;
import frsf.cidisi.faia.agent.Perception;
import frsf.cidisi.faia.agent.search.SearchBasedAgentState;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * Represent the internal state of the Agent.
 */
public class ArqueologoAgentState extends SearchBasedAgentState {
    
    private double capacidadRestante;
    private String origen;
    private String posicionActual;
    private String destino;
    private Mundo mundo;
    private ArrayList<Cofre> acoplado;
    private ArrayList<String> aldeasVisitadas;
    private int disMin;
    private int disRecorrida;
    private int[][] distanciasMin;
//	private int percepcionCofre;
    public Index resultado;
    
    public ArqueologoAgentState(Index r) {
        this.initState(r);
    }

    /**
     * This method is optional, and sets the initial state of the agent.
     */
    public void initState(Index r) {
        
        this.capacidadRestante = 450.0;
        this.origen = Aldea.ARAGAEY;
        this.posicionActual = this.getOrigen();
        this.destino = Aldea.CENEDE;
        this.acoplado = new ArrayList<Cofre>();
        this.mundo = new Mundo();
        this.aldeasVisitadas = new ArrayList<String>();
        aldeasVisitadas.add(posicionActual);
        this.disMin = -1;
        this.disRecorrida = 0;
        this.distanciasMin = mundo.distanciasMinimas();
//      this.percepcionCofre=ArqueologoPerception.UNKNOWN_PERCEPTION;
        this.resultado = r;
    }

    /**
     * This method clones the state of the agent. It's used in the search
     * process, when creating the search tree.
     */
    @Override
    public SearchBasedAgentState clone() {
        String posTemp = this.posicionActual;
        ArrayList<String> visitedPosition = new ArrayList<String>();
        for (String i : aldeasVisitadas) {
            visitedPosition.add(i);
        }
        double capTemp = this.capacidadRestante;
        ArrayList<Cofre> acTemp = new ArrayList<Cofre>(this.acoplado);
        for (Cofre i : acoplado) {
            acTemp.add((Cofre) i.clone());
        }
        ArqueologoAgentState newState = new ArqueologoAgentState(resultado);
        newState.setPosicion(posTemp);        
        newState.setAldeasVisitadas(visitedPosition);        
        newState.setCapacidadRestante(capTemp);
        newState.setcarga(acTemp);
        newState.mundo = (Mundo) this.mundo.clone();
//      newState.percepcionCofre=this.percepcionCofre;
        newState.disMin = this.disMin;
        newState.disRecorrida = this.disRecorrida;
        
        return newState;
    }

    /**
     * This method is used to update the Agent State when a Perception is
     * received by the Simulator.
     */
    @Override
    public void updateState(Perception p) {
//    	ArqueologoPerception arqPerc = (ArqueologoPerception) p;
//    	this.mundo.getAldea(posicionActual).setPercepcion(arqPerc.getVerCofre());
//    	this.percepcionCofre=arqPerc.getVerCofre();
    }

    /**
     * This method returns the String representation of the agent state.
     */
    @Override
    public String toString() {
        String str = "";
        
        str = str + "Posici�n=\"" + posicionActual + "\"\n";
        str = str + "Capacidad restante=\"" + capacidadRestante + "\"\n";
        str = str + "Carga del acoplado=\"" + this.cargaTotal() + "\"\n";
        str = str + "Valor del acoplado=\"" + this.valorTotal() + "\"\n";
        str = str + "Cantidad de cofres=\"" + this.acoplado.size() + "\"\n";
        str = str + "Distancia recorrida=\"" + this.disRecorrida + "\"\n";
        str = str + "Mundo=\" \n";
        /**
         * Para imprimir las adyacencias de las ditintas aldeas.
         */
//        for (int row = 0; row < 11; row++) {
//            str = str + "[ ";
//            for (int col = 0; col < 11; col++) {
//            	str = str + mundo.getDistancias()[row][col] + " ";
//            }
//            str = str + " ]\n";
//        }
        str = str + " \"";
        str = str + "Aldeas visitadas: ";
        Iterator<String> it = aldeasVisitadas.iterator();
        while (it.hasNext()) {
            str = str + it.next() + "; ";
        }        
        return str;
    }

    /**
     * This method is used in the search process to verify if the node already
     * exists in the actual search.
     */
    @Override
    public boolean equals(Object obj) {
        
        if (!(obj instanceof ArqueologoAgentState)) {
            return false;
        }
        
        String pos = ((ArqueologoAgentState) obj).getPosicion();
        double cap = ((ArqueologoAgentState) obj).getCapacidadRestante();
        
        if ((this.posicionActual.equals(pos))
                && (this.capacidadRestante == cap)) {
            return true;
        }
        return false;
    }

    // The following methods are agent-specific:

    public double getCapacidadRestante() {
        return capacidadRestante;
    }

    public void setCapacidadRestante(double arg) {
        capacidadRestante = arg;
    }

    public String getOrigen() {
        return origen;
    }

    public void setOrigen(String origen) {
        this.origen = origen;
    }

    public String getPosicion() {
        return posicionActual;
    }

    public void setPosicion(String arg) {
        posicionActual = arg;
    }

    public String getDestino() {
        return destino;
    }

    public void setDestino(String arg) {
        destino = arg;
    }

    public Mundo getmundo() {
        return mundo;
    }

    public ArrayList<Cofre> getcarga() {
        return acoplado;
    }

    public void setcarga(ArrayList<Cofre> arg) {
        acoplado = arg;
    }

    public double cargaTotal() {
        double cargaT = 0.0;
        Iterator<Cofre> it = acoplado.iterator();
        
        while (it.hasNext()) {
            cargaT += it.next().getPeso();
        }
        return cargaT;        
    }

    /**
     * Funcion que retorna el valor total de los tesoros que lleva el agente en
     * el acoplado.
     *
     * @return valorT
     */
    public int valorTotal() {
        int valorT = 0;
        Iterator<Cofre> it = acoplado.iterator();
        
        while (it.hasNext()) {
            valorT += it.next().getValor();
        }
        return valorT;        
    }

    /**
     * Resibe como parametro un elemento de la clase Cofre, y lo agrega al
     * acoplado del agente.
     *
     * @param co
     */
    public void agregarCofre(Cofre co) {
        this.acoplado.add(co);
    }
    
    public ArrayList<String> getAldeasVisitadas() {
        return aldeasVisitadas;
    }
    
    public void setAldeasVisitadas(ArrayList<String> aldeasVisitadas) {
        this.aldeasVisitadas = aldeasVisitadas;
    }
    /*public int getPercepcionCofre() {
     return percepcionCofre;
     }
     public void setPercepcionCofre(int percepcionCofre) {
     this.percepcionCofre = percepcionCofre;
     }*/

    /**
     * Esta funcion retorna una ArrayList de Strings con los nombres de los
     * vecinos de la posicion actual.
     *
     * @return vec
     */
    public Collection<String> getVecinos() {
        ArrayList<String> vec = new ArrayList<String>();
        int indice = this.mundo.getNombres().indexOf(posicionActual);
        int dist[][] = this.mundo.getDistancias();
        
        for (int i = 0; i < 11; i++) {
            if (dist[indice][i] > 0) {
                vec.add(this.mundo.getNombres().get(i));
            }
        }
        return vec;
    }

    public int getDisMin() {
        return disMin;
    }

    public int getDisRecorrida() {        
        return disRecorrida;
    }

    public void sumarDistancia(int dis) {
        disRecorrida += dis;
    }

    public void agregarAldeaVisitada(String string) {
        this.aldeasVisitadas.add(string);
    }

    public ArrayList<Cofre> getAcoplado() {
        return acoplado;
    }

    public int getDistMin() {
        int indiceA = this.mundo.getNombres().indexOf(posicionActual);
        int indiceD = this.mundo.getNombres().indexOf(destino);
        return distanciasMin[indiceA][indiceD];
    }

    public String ultimaAldeaVisitada() {
        int cantAl = this.aldeasVisitadas.size();
        if (cantAl > 1) {
            return this.aldeasVisitadas.get(cantAl - 2);            
        }
        return this.aldeasVisitadas.get(cantAl - 1);
    }

    public void quitarUltimaAldeaVisitada() {
        int cantAl = this.aldeasVisitadas.size();
        if (cantAl > 1) {
            this.aldeasVisitadas.remove(cantAl - 1);
        }
    }

    public void visualizarResultado(){
        this.resultado.setVisible(true);
    }
    
    @Override
    public void initState() {
//        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    public void marcarVisitas(ArrayList<String> aldeasVisitadas) {
        this.resultado.marcarVisitas(aldeasVisitadas);
        this.armarEstado(this.posicionActual);
    }
    
    public void armarEstado(String s){
        ArrayList<String> estado = new ArrayList<String>();
        estado.add(s);
        estado.add("" + this.getDisRecorrida());
        estado.add("" +this.valorTotal());
        this.resultado.agregarEstado(estado);
    }
    
    public void visualizarResultadoFinal(){
        this.resultado.visualizarEstadoFinal(this.disRecorrida, this.valorTotal());
    }
}

